package com.bruip.spring.aop.annotation;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

import java.util.Arrays;

/**
 * @author 王若龙
 * @CreateTime 2022-09-01 16:16
 * <p>
 * 在切面中，需要通过指定的注解将方法标识为通知方法
 * @Before： 前置通知，在目标对象方法执行之前执行
 * @After： 后置通知，在目标对象方法的finally字句中执行
 * @AfterReturning： 返回通知，在目标对象的返回值之后执行
 * @AfterThrowing: 异常通知，
 * <p>
 * 切入点表达式：设置在标识通知的注解的value属性中
 * <p>
 * 获取连接点信息
 * 在通知方法的参数位置，设置JoinPoint类型的参数，就可以获取连接点所对应的方法的信息
 * //获取连接点对应的方法的签名信息
 * Signature signature = joinPoint.getSignature();
 * //参数列表
 * Object[] args = joinPoint.getArgs();
 *
 * 切面的优先级
 * 可以通过@Order注解的value属性值设置优先级，默认值为Integer。MAX_VALUE
 * @Order 注解的value属性越小，优先级越高
 */
@Component
@Aspect //将当前组件标识为切面
public class LoggerAspect {

    /**
     * 声明一个公共的切入点表达式
     */
    @Pointcut("execution(* com.bruip.spring.aop.annotation.*.*(..))")
    public void pointCut() {
    }


    /**
     * @Before("execution(public int com.bruip.spring.aop.annotation.CalculatorImpl.add(int,int))")
     */
    @Before("pointCut()")
    public void beforeAdviceMethod(JoinPoint joinPoint) {
        //获取连接点对应的方法的签名信息
        Signature signature = joinPoint.getSignature();
        //参数列表
        Object[] args = joinPoint.getArgs();

        System.out.println("LoggerAspect, 前置，方法：" + signature.getName() + "，参数" + Arrays.toString(args));
    }

    @After("pointCut()")
    public void afterAdviceMethod() {
        System.out.println("LoggerAspect，后置通知");
    }

    /**
     * 在返回通知中若要获取目标对象方法的返回值，只需要通过 @AfterReturning 注解的returning属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的返回值的参数
     *
     * @param joinPoint
     * @param result
     */
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturningAdviceMethod(JoinPoint joinPoint, Object result) {
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect, 返回 方法：" + signature.getName() + ", 结果：" + result);
    }

    /**
     * 在返回通知中若要获取目标对象方法的异常信息，只需要通过 @AfterThrowing 注解的 throwing 属性
     * 就可以将通知方法的某个参数指定为接收目标对象方法的异常的参数
     *
     * @param joinPoint
     * @param e
     */
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowingAdviceMethod(JoinPoint joinPoint, Exception e) {
        Signature signature = joinPoint.getSignature();
        System.out.println("LoggerAspect, 方法：" + signature.getName() + "异常：" + e);
    }

    /**
     * 环绕通知的方法和目标方法的返回值一致
     * @param joinPoint
     * @return
     */
    @Around("pointCut()")
    public Object aroundAdviceMethod(ProceedingJoinPoint joinPoint) {
        Object result = null;
        try {
            System.out.println("环绕通知 --> 前置通知");
            //目标对象方法的执行
            result = joinPoint.proceed();
            System.out.println("环绕通知 --> 返回通知");
        } catch (Throwable e) {
            e.printStackTrace();
            System.out.println("环绕通知 --> 异常通知");
        } finally {
            System.out.println("环绕通知 --> 后置通知");
        }
        return result;
    }
}
